#pragma once

#include <memory>
#include <string>
#include <unordered_map>

#include "Base.h"

namespace streamdb {

template<typename T>
class NamedObserver: public IObserver<T> , public Nameable{
public:
    NamedObserver(const std::string & name) : Nameable(name){}
    ~NamedObserver() override {}
    
private:
};

template<typename T>
class NamedSubject : public ISubject<T, std::shared_ptr<NamedObserver<T>>>, public Nameable{
public:
    NamedSubject(const std::string & name) : Nameable(name) {}
    ~NamedSubject() override {}

    bool attach(const std::shared_ptr<NamedObserver<T>> & observer) override{
        auto it = observers.find(observer->getName());
        if(it != observers.end()){
            it->second = observer;
        }
        else{
            observers.insert(std::make_pair(observer->getName(), observer));
        }
        return true;
    }

    void detach(const std::shared_ptr<NamedObserver<T>> & observer) override{
        auto it = observers.find(observer->getName());
        if(it != observers.end()){
            observers.erase(it);
        }
    }

    void notify(const T & object) const override{
        for(auto & p : observers){
            p.second->update(object);
        }
    }
private:
    std::unordered_map<std::string, std::shared_ptr<NamedObserver<T>>> observers;
};



/* 

enum SubjectType : public char{
    kAppend = 0,
    kRandom = 1,
};
class DB {
public:
    virtual ~DB(){}
    static Status Open(std::unique_ptr<DB> & db);

    virtual Status makeSubject(SubjectType type, const std::string & name, std::unique_ptr<Subject> & sub) = 0;
    virtual Status getSubject(const std::string & name, std::unique_ptr<Subject> & sub) = 0;

    virtual void close() = 0;
};

void test(){
    std:unique_ptr<DB> db;
    auto s = streamdb::DB::Open(db);
    if(s.ok()){
        std::unique_ptr<Subject> sub;
        s = db->makeSubject(SubjectType::kAPPEND, name, sub);
        if(s.ok()){
            sub->notify("hello0");
            sub->notify("hello1");
            sub->notify("hello2");
            sub->notify("hello3");
            sub->notify("hello4");
            sub->notify("hello5");
        }
        //delete sub
    }
    //delete db
}

void testRead(){
    std::unique_ptr<DB> db;
    auto s = streamdb::DB::Open(db);
    if(s.ok()){
        std::unique_ptr<Subject> sub;
        s = db->getSubject(SubjectType::kAPPEND, name, sub);
        if(s.ok()){
            auto obs = std::make_shared<Observer>("observer");
            sub->attach(obs);
            sleep(1000);
            sub->remove(obs.getId());
        }
    }
}
*/

}//namespace streamdb

